🎖️GitЯра🎖️
scripts/check-metadata-length.py fix/qr-error-correction (7b45f84f) Text, 2.87 KB
T8b949e#!/usr/bin/env python3
T8b949e"""Validate Fastlane store-listing metadata against store length limits.
The ``fastlane/metadata/android`` tree is a mirror of Crowdin: translations are
downloaded on a schedule (see ``.github/workflows/scheduled-updates.yml``).
Crowdin's max-length toggle only blocks *new* submissions; translations entered
before enforcement was enabled are grandfathered in and keep syncing down. This
script is the repo-side guard that catches them regardless of Crowdin state.
Lengths are measured in Unicode code points (what Google Play and F-Droid /
IzzyOnDroid count), not bytes -- a byte count badly over-reports Cyrillic and
CJK strings.
Exit status is non-zero if any file exceeds its limit, so it can gate CI.
"""
Tff7b72from T7ee787__future__ Tff7b72import Te6edf3annotations
Tff7b72import T7ee787os
Tff7b72import T7ee787sys
Tff7b72from T7ee787pathlib Tff7b72import Te6edf3Path
T8b949e# Repo root = parent of this script's directory (scripts/).
Te6edf3REPO_ROOT Tff7b72= Te6edf3PathTb4b4b4(Tff7b72__file__Tb4b4b4)Tff7b72.Td2a8ffresolveTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffparentTff7b72.Td2a8ffparent
Te6edf3METADATA_DIR Tff7b72= Te6edf3REPO_ROOT Tff7b72/ Ta5d6ff"Ta5d6fffastlaneTa5d6ff" Tff7b72/ Ta5d6ff"Ta5d6ffmetadataTa5d6ff" Tff7b72/ Ta5d6ff"Ta5d6ffandroidTa5d6ff"
T8b949e# Per-file character limits. Keys are file names under each locale directory.
T8b949e# 80 is the F-Droid summary / Google Play short-description limit; 30 is the
T8b949e# Play title limit. Add more entries here to extend coverage.
Te6edf3LIMITS Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffshort_description.txtTa5d6ff"Tb4b4b4: T79c0ff80Tb4b4b4,
Ta5d6ff"Ta5d6fftitle.txtTa5d6ff"Tb4b4b4: T79c0ff30Tb4b4b4,
Tb4b4b4}
T8b949e# Running inside GitHub Actions enables ::error:: annotations on the PR.
Te6edf3IN_GITHUB_ACTIONS Tff7b72= Te6edf3osTff7b72.Td2a8ffenvironTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffGITHUB_ACTIONSTa5d6ff"Tb4b4b4) Tff7b72== Ta5d6ff"Ta5d6fftrueTa5d6ff"
Tff7b72def Td2a8ffchar_countTb4b4b4(Te6edf3pathTb4b4b4: Te6edf3PathTb4b4b4) Tff7b72-Tff7b72> Tffa657intTb4b4b4:
T8b949e"""Code-point length of a metadata file, ignoring trailing whitespace."""
Tff7b72return Tffa657lenTb4b4b4(Te6edf3pathTff7b72.Td2a8ffread_textTb4b4b4(Te6edf3encodingTff7b72=Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8ffrstripTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72def Td2a8ffmainTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657intTb4b4b4:
Tff7b72if Tff7b72not Te6edf3METADATA_DIRTff7b72.Td2a8ffis_dirTb4b4b4(Tb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6fferror: metadata directory not found: Tffd700{Te6edf3METADATA_DIRTffd700}Ta5d6ff"Tb4b4b4, Te6edf3fileTff7b72=Te6edf3sysTff7b72.Td2a8ffstderrTb4b4b4)
Tff7b72return T79c0ff2
Te6edf3violationsTb4b4b4: Tffa657listTb4b4b4[Tffa657tupleTb4b4b4[Te6edf3PathTb4b4b4, Tffa657intTb4b4b4, Tffa657intTb4b4b4]Tb4b4b4] Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3file_nameTb4b4b4, Te6edf3limit Tff7b72in Tffa657sortedTb4b4b4(Te6edf3LIMITSTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4:
Tff7b72for Te6edf3path Tff7b72in Tffa657sortedTb4b4b4(Te6edf3METADATA_DIRTff7b72.Td2a8ffglobTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ff*/Tffd700{Te6edf3file_nameTffd700}Ta5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4:
Te6edf3count Tff7b72= Te6edf3char_countTb4b4b4(Te6edf3pathTb4b4b4)
Tff7b72if Te6edf3count Tff7b72> Te6edf3limitTb4b4b4:
Te6edf3violationsTff7b72.Td2a8ffappendTb4b4b4(Tb4b4b4(Te6edf3pathTb4b4b4, Te6edf3countTb4b4b4, Te6edf3limitTb4b4b4)Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3violationsTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffAll store-listing metadata is within length limits.Ta5d6ff"Tb4b4b4)
Tff7b72return T79c0ff0
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffStore-listing metadata exceeds length limits:Tffea00\nTa5d6ff"Tb4b4b4)
Tff7b72for Te6edf3pathTb4b4b4, Te6edf3countTb4b4b4, Te6edf3limit Tff7b72in Te6edf3violationsTb4b4b4:
Te6edf3rel Tff7b72= Te6edf3pathTff7b72.Td2a8ffrelative_toTb4b4b4(Te6edf3REPO_ROOTTb4b4b4)
Te6edf3message Tff7b72= Ta5d6fffTa5d6ff"Tffd700{Te6edf3relTffd700}Ta5d6ff is Tffd700{Te6edf3countTffd700}Ta5d6ff chars (limit Tffd700{Te6edf3limitTffd700}Ta5d6ff)Ta5d6ff"
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ff - Tffd700{Te6edf3messageTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72if Te6edf3IN_GITHUB_ACTIONSTb4b4b4:
T8b949e# Annotate the offending file directly in the PR diff view.
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ff::error file=Tffd700{Te6edf3relTffd700}Ta5d6ff::Tffd700{Te6edf3messageTffd700}Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(
Ta5d6ff"Tffea00\nTa5d6ffThese files are mirrored from Crowdin. Fix them at the source Ta5d6ff"
Ta5d6ff"Ta5d6ff(shorten or remove the overlength translation so it is re-translated), Ta5d6ff"
Ta5d6ff"Ta5d6ffthen re-sync -- editing the mirror here is overwritten on the next sync.Ta5d6ff"
Tb4b4b4)
Tff7b72return T79c0ff1
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Tff7b72raise Tf85149SystemExitTb4b4b4(Te6edf3mainTb4b4b4(Tb4b4b4)Tb4b4b4)
Served by rngit 1.5.0 - Generated in 0.08s